// Copyright (C) joyinter
// All rights reserved.
// 
// Author : Jiang Li
// Date   : 2004-11-10

#include "stdafx.h"
#include "svr_user.h"
#include "svr_server.h"
#include "svr_userset.h"
#include "svr_asyncqueue.h"
#include "svr_netmsg.h"

namespace JoyServer
{
;
class CJoyKickUserMsg : public CJoyUserSetMsg
{
protected:
    virtual void OnExecute(CJoyUser* pUser)
    {
        pUser->DoKick();
    }
public:
    CJoyKickUserMsg()
    {
    }
    virtual ~CJoyKickUserMsg()
    {
    }
};

// class CJoyUserAsyncObject

void CJoyUserAsyncObject::Execute()
{
    if(++m_asyncState > 0)
    {
        try
        {
            OnExecute();
        }
        catch(EUserExit)
        {
        }
    }
    -- m_asyncState;
}

void CJoyUserAsyncObject::CheckUserStatus()
{
    if(m_user->m_status == UserStatus_Exit)
    {
        throw EUserExit();
    }
}

// class CJoyUser

CJoyUser::CJoyUser()
{
    m_userSet   = NULL;
    m_asyncQueue= NULL;
    m_userId    = 0;
    m_troop     = UserTroop_User;
    m_sex       = UserSex_None;
    m_status    = UserStatus_None;
    memset(&m_userAddr, 0, sizeof(m_userAddr));
}

CJoyUser::~CJoyUser()
{
    if(m_asyncQueue != NULL)
    {
        m_asyncQueue->Release();
    }
}

bool CJoyUser::CanRelease()
{
    if(!m_asyncQueue->IsEmpty())
    {
        return false;
    }
    return true;
}

CString CJoyUser::GetUserIP() const
{
    CString strIP;
    strIP.Format("%d.%d.%d.%d", 
        m_userAddr.sin_addr.S_un.S_un_b.s_b1,
        m_userAddr.sin_addr.S_un.S_un_b.s_b2,
        m_userAddr.sin_addr.S_un.S_un_b.s_b3,
        m_userAddr.sin_addr.S_un.S_un_b.s_b4);
    return strIP;
}

bool CJoyUser::Create(CJoyUserSet* userSet, SOCKET s, const sockaddr_in& addr)
{
    if(!m_socket.Attach(s))
    {
        return false;
    }

    m_userAddr = addr;

    m_asyncQueue = AllocAsyncQueue();
    if(!m_asyncQueue->Create(userSet->m_server->m_asyncExec))
    {
        return false;
    }

    m_status = UserStatus_Connected;
    m_tick   = GetTickCount();

    CJoyAutoCS gate(userSet->m_userLock);

    if(!userSet->UserEnter(this))
    {
        return false;
    }

    UINT userCount = ++ userSet->m_server->m_userCount_All;
    UINT userMax = userSet->m_server->m_userCount_Max;
    if(userMax != 0)
    {
        if(userCount > userMax)
        {
            Kick(JoyMessage::Error_UserMax);
        }
    }

    // begin recv datas.
    m_socket.QueueRecv();

    return true;
}

void CJoyUser::Destroy()
{
    if(m_userSet != NULL)
    {
        -- m_userSet->m_server->m_userCount_All;
    }

    LeaveUserSet();
    m_socket.Close();
    if(m_asyncQueue != NULL)
    {
        m_asyncQueue->Destroy();
        m_asyncQueue->Release();
        m_asyncQueue = NULL;
    }
}

bool CJoyUser::SendMsg(JoyMessage::MessageBase* msg)
{
    CJoyTransmitData* data = JoyMsgTool::BuildMsgData(msg);
    bool b = m_socket.Send(data);
    data->Release();
    return b;
}

bool CJoyUser::SendMsg(CJoyTransmitData* data)
{
    return m_socket.Send(data);
}

void CJoyUser::OnKick()
{
}

void CJoyUser::DoKick()
{
    CJoyServer* pServer = GetServer();
    m_status    = UserStatus_Exit;
    OnKick();
    pServer->QueueReleaseUser(this);
}

void CJoyUser::Kick(UINT nErr)
{
    if(++m_kicked > 1)
    {
        return;
    }
    m_socket.StopRecv();
    if(nErr != 0)
    {
        JoyMessage::MS_Error err;
        err.m_error = (JoyMessage::ErrorType)nErr;
        m_socket.SendMsg(&err);
    }
    QueueUserSetMsg(new CJoyKickUserMsg());
}

void CJoyUser::QueueUserSetMsg(CJoyUserSetMsg* pMsg)
{
    CJoyUserSet* userSet = m_userSet;
    if(userSet == NULL)
    {
        delete pMsg;
        return;
    }
    pMsg->m_user    = this;
    userSet->QueueMsg(pMsg);
}

void CJoyUser::QueueAsyncObject(CJoyAsyncObject* pObj)
{
    m_asyncQueue->QueueObject(pObj);
}

CJoyServer* CJoyUser::GetServer()
{
    return m_userSet->m_server;
}

void CJoyUser::Lock()
{
}

void CJoyUser::Unlock()
{
}

void CJoyUser::EnterUserSet(CJoyUserSet* userSet)
{
    userSet->UserEnter(this);
}

void CJoyUser::LeaveUserSet()
{
    CJoyUserSet* userSet = m_userSet;
    if(userSet != NULL)
    {
        userSet->UserLeave(this);
    }
}

void CJoyUser::NetMsg_BadData(const JoyMessage::MessageBase* msg)
{
    msg;
    OnError(UserError_SockBadData);
}

void CJoyUser::OnSockError(int err)
{
    err;
    OnError(UserError_SockClose);
}

void CJoyUser::OnError(UserError nError)
{
    switch(nError)
    {
    case UserError_SockBadData:
        Kick(JoyMessage::Error_BadData);
        break;
    case UserError_AlreadyOnline:
        Kick(JoyMessage::Error_AlreadyOnline);
        break;
    case UserError_NotLogin:
        Kick(JoyMessage::Error_NotLogin);
        break;
    case UserError_NamePass:
        Kick(JoyMessage::Error_NamePass);
        break;
    case UserError_Banned:
        Kick(JoyMessage::Error_Banned);
        break;
    case UserError_UserMax:
        Kick(JoyMessage::Error_UserMax);
        break;
    case UserError_BadVersion:
        Kick(JoyMessage::Error_BadVersion);
        break;
    case UserError_Kicked:
        Kick(JoyMessage::Error_Kicked);
        break;
    case UserError_LevelLow:
        Kick(JoyMessage::Error_LevelLow);
        break;
    case UserError_CreditLow:
        Kick(JoyMessage::Error_CreditLow);
        break;

    // all the following use default kick.
    case UserError_Failed:
    default:
        Kick();
    }
}

CJoyAsyncQueue* CJoyUser::AllocAsyncQueue()
{
    return new CJoyAsyncQueue();
}

} // namespace JoyServer
